#include "wx/wxprec.h"

#include <string.h>

#if defined(__WXDEBUG__) || wxUSE_DEBUG_CONTEXT
#if defined(__VISAGECPP__)
#define DEBUG_PRINTF(NAME) { static int raz=0; \
    printf( #NAME " %i\n",raz); fflush(stdout); raz++; }
#else
#define DEBUG_PRINTF(NAME)
#endif
#endif // __WXDEBUG__ || wxUSE_DEBUG_CONTEXT

#if defined __VISUALC__ && __VISUALC__ >= 1300
#pragma optimize("", off)
#endif

wxClassInfo wxObject::ms_classInfo( wxT( "wxObject" ), 0, 0,
                                    ( int ) sizeof( wxObject ),
                                    ( wxObjectConstructorFn ) 0 );

// restore optimizations
#if defined __VISUALC__ && __VISUALC__ >= 1300
#pragma optimize("", on)
#endif

wxClassInfo* wxClassInfo::sm_first = NULL;
wxHashTable* wxClassInfo::sm_classTable = NULL;

#if !wxUSE_EXTENDED_RTTI

wxClassInfo *wxObject::GetClassInfo() const {
  return &wxObject::ms_classInfo;
}

#endif // wxUSE_EXTENDED_RTTI

// this variable exists only so that we can avoid 'always true/false' warnings
const bool wxFalse = false;

// Is this object a kind of (a subclass of) 'info'?
// E.g. is wxWindow a kind of wxObject?
// Go from this class to superclass, taking into account
// two possible base classes.
bool wxObject::IsKindOf( wxClassInfo *info ) const {
  wxClassInfo *thisInfo = GetClassInfo();
  return ( thisInfo ) ? thisInfo->IsKindOf( info ) : false ;
}

#if defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING && defined( new )
#undef new
#endif


#ifdef _WX_WANT_NEW_SIZET_WXCHAR_INT
void *wxObject::operator new( size_t size, const wxChar *fileName, int lineNum ) {
  return wxDebugAlloc( size, ( wxChar* ) fileName, lineNum, true );
}
#endif

#ifdef _WX_WANT_DELETE_VOID
void wxObject::operator delete( void *buf ) {
  wxDebugFree( buf );
}
#endif

#ifdef _WX_WANT_DELETE_VOID_CONSTCHAR_SIZET
void wxObject::operator delete( void *buf, const char *_fname, size_t _line ) {
  wxDebugFree( buf );
}
#endif

#ifdef _WX_WANT_DELETE_VOID_WXCHAR_INT
void wxObject::operator delete( void *buf, const wxChar *( fileName ), int ( lineNum ) ) {
  wxDebugFree( buf );
}
#endif

#ifdef _WX_WANT_ARRAY_NEW_SIZET_WXCHAR_INT
void *wxObject::operator new[]( size_t size, const wxChar* fileName, int lineNum ) {
  return wxDebugAlloc( size, ( wxChar* ) fileName, lineNum, true, true );
}
#endif

#ifdef _WX_WANT_ARRAY_DELETE_VOID
void wxObject::operator delete[]( void *buf ) {
  wxDebugFree( buf, true );
}
#endif

#ifdef _WX_WANT_ARRAY_DELETE_VOID_WXCHAR_INT
void wxObject::operator delete[]( void * buf, const wxChar*  ( fileName ), int ( lineNum ) ) {
  wxDebugFree( buf, true );
}
#endif

wxClassInfo::~wxClassInfo() {
  if( this == sm_first ) {
    sm_first = m_next;
  } else {
    wxClassInfo *info = sm_first;
    while( info ) {
      if( info->m_next == this ) {
        info->m_next = m_next;
        break;
      }
      info = info->m_next;
    }
  }
  Unregister();
}

wxClassInfo *wxClassInfo::FindClass( const wxChar *className ) {
  if( sm_classTable ) {
    return ( wxClassInfo * )wxClassInfo::sm_classTable->Get( className );
  } else {
    for( wxClassInfo *info = sm_first; info ; info = info->m_next ) {
      if( wxStrcmp( info->GetClassName(), className ) == 0 ) {
        return info;
      }
    }
    return NULL;
  }
}

void wxClassInfo::Register() {
  #ifdef __WXDEBUG__
  // reentrance guard - see note above
  static int entry = 0;
  #endif
  wxHashTable *classTable;
  if( !sm_classTable ) {
    // keep the hash local initially, reentrance is possible
    classTable = new wxHashTable( wxKEY_STRING );
  } else {
    // guard againt reentrance once the global has been created
    wxASSERT_MSG( ++entry == 1, _T( "wxClassInfo::Register() reentrance" ) );
    classTable = sm_classTable;
  }
  wxASSERT_MSG( classTable->Get( m_className ) == NULL,
                wxString::Format
                (
                  _T( "Class \"%s\" already in RTTI table - have you used IMPLEMENT_DYNAMIC_CLASS() multiple times or linked some object file twice)?" ),
                  m_className
                )
              );
  classTable->Put( m_className, ( wxObject * )this );
  if( sm_classTable != classTable ) {
    if( !sm_classTable ) {
      sm_classTable = classTable;
    } else {
      delete classTable;
      Register();
    }
  }
  #ifdef __WXDEBUG__
  entry = 0;
  #endif
}

void wxClassInfo::Unregister() {
  if( sm_classTable ) {
    sm_classTable->Delete( m_className );
    if( sm_classTable->GetCount() == 0 ) {
      delete sm_classTable;
      sm_classTable = NULL;
    }
  }
}

wxObject *wxCreateDynamicObject( const wxChar *name ) {
  #if defined(__WXDEBUG__) || wxUSE_DEBUG_CONTEXT
  DEBUG_PRINTF( wxObject * wxCreateDynamicObject )
  #endif
  if( wxClassInfo::sm_classTable ) {
    wxClassInfo *info = ( wxClassInfo * )wxClassInfo::sm_classTable->Get( name );
    return info ? info->CreateObject() : NULL;
  } else { // no sm_classTable yet
    for( wxClassInfo *info = wxClassInfo::sm_first;
         info;
         info = info->m_next ) {
      if( info->m_className && wxStrcmp( info->m_className, name ) == 0 ) {
        return info->CreateObject();
      }
    }
    return NULL;
  }
}

void wxObject::Ref( const wxObject& clone ) {
  #if defined(__WXDEBUG__) || wxUSE_DEBUG_CONTEXT
  DEBUG_PRINTF( wxObject::Ref )
  #endif
  // nothing to be done
  if( m_refData == clone.m_refData ) {
    return;
  }
  // delete reference to old data
  UnRef();
  // reference new data
  if( clone.m_refData ) {
    m_refData = clone.m_refData;
    ++( m_refData->m_count );
  }
}

void wxObject::UnRef() {
  if( m_refData ) {
    wxASSERT_MSG( m_refData->m_count > 0, _T( "invalid ref data count" ) );
    if( --m_refData->m_count == 0 ) {
      delete m_refData;
    }
    m_refData = NULL;
  }
}

void wxObject::AllocExclusive() {
  if( !m_refData ) {
    m_refData = CreateRefData();
  } else if( m_refData->GetRefCount() > 1 ) {
    const wxObjectRefData* ref = m_refData;
    UnRef();
    m_refData = CloneRefData( ref );
  }
  wxASSERT_MSG( m_refData && m_refData->GetRefCount() == 1,
                _T( "wxObject::AllocExclusive() failed." ) );
}

wxObjectRefData *wxObject::CreateRefData() const {
  wxFAIL_MSG( _T( "CreateRefData() must be overridden if called!" ) );
  return NULL;
}

wxObjectRefData * wxObject::CloneRefData( const wxObjectRefData * ( data ) ) const {
  wxFAIL_MSG( _T( "CloneRefData() must be overridden if called!" ) );
  return NULL;
}
